import SparkMD5 from 'spark-md5';
import { calculateCRC16 } from './crc'
import { Bluetooth, TransferDataFn, EmitEvent, EventDataMap } from './bluetooth';
import { PROTOCOL, Operation, AuthStatus, UpgradeFlag, OperationStatus, RemoteControllerInfoType } from './constant';

/** 读取/写入长度选项（严格限制标准值） */
export type DataLength = 16 | 32 | 64 | 128;
export type OperationParamMap = {
  [Operation.READ]: {
    length: DataLength; // 每次读取长度 (16/32/64/128)
    type: RemoteControllerInfoType; // 信息类型
  };
  [Operation.WRITE]: {
    length: DataLength; // 每次读取长度 (16/32/64/128)
    type: RemoteControllerInfoType; // 信息类型
    dataLength: number; // 数据长度
  };
  [Operation.TERMINATE]: {
    crc: boolean; // CRC16 校验值
    crcValue?: number; // CRC16 校验值
  }; // 终止操作
  [Operation.DATA_TRANSFER]: {
    packetSequence: number; // 包序列号 0x00～0xFF
    dataLength: number; // 每次传输的有效数据长度 0x01～len
    data: Uint8Array; // 数据内容
  }; // 数据传输
  [Operation.DATA_ACK]: {};
  [Operation.GET_FLAG]: {}; // 获取升级标志
  [Operation.SET_FLAG]: {
    flag: UpgradeFlag; // 升级标志
  };
  [Operation.HOST_AUTH]: {
    hash: Uint8Array
  }; // 主机认证
  [Operation.DEVICE_AUTH]: {
    status: AuthStatus; // 认证识别码
    keyIndex: number; // Key index
    random: Uint8Array; // 随机数
  };
};
export type OperationResponseMap = {
  [Operation.READ]: {
    status: OperationStatus; // 操作状态码
    address: number; // 地址
    length: number; // 数据长度
  };
  [Operation.WRITE]: {
    status: OperationStatus; // 操作状态码
    address: number; // 地址
    length: number; // 数据长度
  };
  [Operation.TERMINATE]: {
    status: OperationStatus; // 操作状态码
    crc16: number; // CRC16 校验值
  };
  [Operation.DATA_TRANSFER]: {
    packetSequence: number; // 包序列号 0x00～0xFF
    dataLength: number; // 每次传输的有效数据长度 0x01～len
    packetData: Uint8Array; // 数据内容 0x01～len
  };
  [Operation.DATA_ACK]: {};
  [Operation.GET_FLAG]: {
    status: OperationStatus; // 操作状态码
    flag: UpgradeFlag; // 升级标志
  };
  [Operation.SET_FLAG]: {
    status: OperationStatus; // 操作状态码
  };
  [Operation.HOST_AUTH]: {
    status: AuthStatus; // 认证状态
    keyIndex?: number;
    rand?: Uint8Array; // 随机数
    idCode?: Uint8Array; // ID 码
  };
  [Operation.DEVICE_AUTH]: {
    hash: Uint8Array; // 16 bytes
  };
};
const MAX_CHUNK_SIZE = 64; // 最大分片大小
class BasicRemoteBluetooth extends Bluetooth {
  private _serviceUUID?: string;
  private _characteristicUUID?: string;
  // ============ 公共方法 ============
  /**
   * 配置蓝牙服务参数
   * @param serviceUUID 服务UUID
   * @param characteristicUUID 特征UUID
   */
  configure(serviceUUID?: string, characteristicUUID?: string): this {
    if (serviceUUID) {
      this._serviceUUID = serviceUUID;
    }
    if (characteristicUUID) {
      this._characteristicUUID = characteristicUUID;
    }
    return this;
  }

  async startCommand<T extends Operation>(operation: T, params: OperationParamMap[T]): Promise<void> {
    this._validateConfiguration();
    const buffer = this._buildRequest(operation, params);
    if (!buffer) {
      throw new Error(`Failed to build request buffer [operation]:${operation}`);
    }
    console.log('startCommand', this.arrayBufferToHexString(buffer));
    await this.write(this._serviceUUID!, this._characteristicUUID!, buffer);
  }

  async startNotification(): Promise<void> {
    this._validateConfiguration();
    return super.startNotification(this._serviceUUID!, this._characteristicUUID!);
  }

  async disconnectWithUnPair(): Promise<void> {
    if (this._serviceUUID && this._characteristicUUID) {
      const clear = await this.unPair(this._serviceUUID, this._characteristicUUID);
      await this.disconnect();
      clear && clear();
    } else {
      await this.disconnect();
    }
  }

  parseResponse<T extends Operation>(data: ArrayBuffer, operation: T): OperationResponseMap[T] {
    const view = new DataView(data);
    console.log('解析响应数据:', this.arrayBufferToHexString(view.buffer));
    this._validateResponseHeader(view, PROTOCOL.OPERATION[operation]);
    switch (operation) {
      case Operation.READ:
      case Operation.WRITE:
        return {
          status: view.getUint8(2),
          address: view.getUint32(3, false),
          length: view.getUint32(7, false)
        } as OperationResponseMap[T];
      case Operation.TERMINATE:
        const crc16High = view.getUint8(3);
        const crc16Low = view.getUint8(4);
        const crc16 = (crc16High << 8) | crc16Low;
        return {
          status: view.getUint8(2),
          crc16: crc16
        } as OperationResponseMap[T];
      case Operation.DATA_TRANSFER:
        const packetSequence = view.getUint8(2);
        const dataLength = view.getUint8(3);
        let packetData = new Uint8Array(0);
        if (dataLength > 0) {
          packetData = new Uint8Array(data.slice(4, 4 + dataLength));
        }
        return {
          packetSequence: packetSequence,
          packetData, // 数据内容
          dataLength
        } as OperationResponseMap[T];
      case Operation.GET_FLAG:
        return {
          status: view.getUint8(2),
          flag: view.getUint32(3, false)
        } as OperationResponseMap[T];
      case Operation.SET_FLAG:
        return {
          status: view.getUint8(2)
        } as OperationResponseMap[T];
      case Operation.HOST_AUTH:
        const hostAuthStatus = view.getUint8(2);
        if (hostAuthStatus !== AuthStatus.INITIATE) {
          return {
            status: hostAuthStatus
          } as OperationResponseMap[T]
        }
        const keyIndex = view.getUint8(3);
        const rand = new Uint8Array(data.slice(4, 20));
        const idCode = new Uint8Array(data.slice(20, 52));
        return {
          status: hostAuthStatus,
          keyIndex,
          rand, // 随机数
          idCode,
        } as OperationResponseMap[T];
      case Operation.DEVICE_AUTH:
        return {
          hash: new Uint8Array(data.slice(2, 18)) // 16 bytes
        } as OperationResponseMap[T];
      default:
        throw new Error(`Unsupported operation: ${operation}`);
    }
  }
  /**
   * 执行命令并等待响应
   * @param operation 操作类型
   * @param params 命令参数
   * @param timeout 超时时间(毫秒)
   * @param extraValidator 额外的响应验证函数
   */
  async executeCommandWithResponse<T extends Operation>(
    operation: T,
    params: OperationParamMap[T],
    timeout: number = 2000,
    extraValidator?: (data: OperationResponseMap[T]) => boolean
  ) {
    return this.withResponseHandler<OperationResponseMap[T]>(
      () => this.startCommand(operation, params),
      (data, succeed, fail) => {
        try {
          const response = this.parseResponse(data.value, operation);
          if (extraValidator && !extraValidator(response)) {
            fail('额外校验失败');
            return;
          }
          succeed(response);
        } catch (error) {
          console.error(
            `解析响应失败，原始数据: ${this.arrayBufferToHexString(data.value)}`,
            error
          );
          // 继续等待有效响应
        }
      },
      { timeout }
    );
  }

  sendRemoteData(bigData: Uint8Array, onBefore?: TransferDataFn,) {
    return new Promise(async (resolve) => {
      const fn: TransferDataFn = async (params) => {
        const { isLastChunk, ...rest } = params;
        if (onBefore) {
          await onBefore(params);
        }
        await this.executeCommandWithResponse(Operation.DATA_TRANSFER, rest, undefined, (data) => {
          if (rest.dataLength !== data.dataLength) {
            console.log('数据长度不一致', rest.dataLength, data.dataLength);
            return false;
          }
          if (rest.packetSequence !== data.packetSequence) {
            console.log('数据包序列号不一致', rest.packetSequence, data.packetSequence);
            return false;
          }
          return true
        });
        if (isLastChunk) {
          await this.sleep(200)
          resolve(undefined);
        }
      };
      await this.send(bigData, fn.bind(this));
    });
  }

  // ============ 私有方法 ============
  private _buildRequest<T extends Operation>(operation: T, params: OperationParamMap[T]) {
    switch (operation) {
      case Operation.READ:
        const readParams = params as OperationParamMap[Operation.READ];
        const readBuffer = new ArrayBuffer(11);
        const readView = new DataView(readBuffer);
        readView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        readView.setUint8(1, PROTOCOL.OPERATION.READ);
        readView.setUint8(2, readParams.length);

        const typeBytes = this.stringToUint8Array(readParams.type);
        const typeUint8Array = new Uint8Array(PROTOCOL.TYPE_LENGTH).fill(0);
        typeUint8Array.set(typeBytes.subarray(0, PROTOCOL.TYPE_LENGTH));
        new Uint8Array(readBuffer).set(typeUint8Array, 3);

        return readBuffer;
      case Operation.WRITE:
        const writeParams = params as OperationParamMap[Operation.WRITE];
        const buffer = new ArrayBuffer(19);
        const view = new DataView(buffer);
        view.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        view.setUint8(1, PROTOCOL.OPERATION.WRITE);
        view.setUint8(2, writeParams.length);

        const wTypeBytes = this.stringToUint8Array(writeParams.type);
        const wTypeUint8Array = new Uint8Array(PROTOCOL.TYPE_LENGTH).fill(0);
        wTypeUint8Array.set(wTypeBytes.subarray(0, PROTOCOL.TYPE_LENGTH));
        new Uint8Array(buffer).set(wTypeUint8Array, 3);

        view.setUint32(11, 0); // 保留字段
        view.setUint32(15, (params as OperationParamMap[Operation.WRITE]).dataLength, false);
        return buffer;
      case Operation.TERMINATE:
        const terParams = params as OperationParamMap[Operation.TERMINATE];
        const terminateBuffer = new ArrayBuffer(4);
        const terminateView = new DataView(terminateBuffer);
        terminateView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        terminateView.setUint8(1, PROTOCOL.OPERATION.TERMINATE);
        if (terParams.crc) {
          const crcValue = terParams.crcValue ?? calculateCRC16(new Uint8Array(terminateBuffer.slice(0, 2)));
          terminateView.setUint8(2, (crcValue >> 8) & 0xff); // High byte
          terminateView.setUint8(3, crcValue & 0xff); // Low byte
        }
        return terminateBuffer;
      case Operation.DATA_TRANSFER:
        const uplinkParams = params as OperationParamMap[Operation.DATA_TRANSFER];
        const uplinkBuffer = new ArrayBuffer(4 + uplinkParams.data.length);
        const transferView = new DataView(uplinkBuffer);
        transferView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        transferView.setUint8(1, PROTOCOL.OPERATION.DATA_TRANSFER);
        transferView.setUint8(2, uplinkParams.packetSequence);
        transferView.setUint8(3, uplinkParams.dataLength);
        new Uint8Array(uplinkBuffer).set(uplinkParams.data, 4);
        return uplinkBuffer;
      // 终止数据传输接口
      case Operation.DATA_ACK:
        const ackBuffer = new ArrayBuffer(4);
        const ackView = new DataView(ackBuffer);
        ackView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        ackView.setUint8(1, PROTOCOL.OPERATION.DATA_TRANSFER);
        ackView.setUint8(2, 0x00);
        ackView.setUint8(3, 0x00); // 保留字段
        return ackBuffer;
      case Operation.GET_FLAG:
        const getFlagBuffer = new ArrayBuffer(2);
        const getFlagView = new DataView(getFlagBuffer);
        getFlagView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        getFlagView.setUint8(1, PROTOCOL.OPERATION.GET_FLAG);
        return getFlagBuffer;
      case Operation.SET_FLAG:
        const setFlagParams = params as OperationParamMap[Operation.SET_FLAG];
        const setFlagBuffer = new ArrayBuffer(6);
        const setFlagView = new DataView(setFlagBuffer);
        setFlagView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        setFlagView.setUint8(1, PROTOCOL.OPERATION.SET_FLAG);
        setFlagView.setUint32(2, setFlagParams.flag, false); // 升级标志
        return setFlagBuffer;
      case Operation.HOST_AUTH:
        const hostAuthParams = params as OperationParamMap[Operation.HOST_AUTH];
        const hostAuthBuffer = new ArrayBuffer(18);
        const hostAuthView = new DataView(hostAuthBuffer);
        hostAuthView.setUint8(0, PROTOCOL.COMMAND.REQUEST); // ??????
        hostAuthView.setUint8(1, PROTOCOL.OPERATION.HOST_AUTH);
        new Uint8Array(hostAuthBuffer, 2, 16).set(hostAuthParams.hash);
        return hostAuthBuffer;
      case Operation.DEVICE_AUTH:
        const deviceAuthParams = params as OperationParamMap[Operation.DEVICE_AUTH];
        const deviceAuthBuffer = new ArrayBuffer(20); // 1+1+1+16+1
        const deviceAuthView = new DataView(deviceAuthBuffer);
        deviceAuthView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        deviceAuthView.setUint8(1, PROTOCOL.OPERATION.DEVICE_AUTH);
        deviceAuthView.setUint8(2, deviceAuthParams.status);
        // Set 16-byte random number (offset 3-18)
        new Uint8Array(deviceAuthBuffer, 3, 16).set(deviceAuthParams.random);
        // Set key index (offset 19)
        deviceAuthView.setUint8(19, deviceAuthParams.keyIndex);

        return deviceAuthBuffer;
      default:
    }
  }

  private _validateConfiguration(): void {
    if (!this._serviceUUID || !this._characteristicUUID) {
      throw new Error('Bluetooth service not configured');
    }
  }

  private _validateResponseHeader(view: DataView, operation: number): void {
    if (view.getUint8(1) !== operation) {
      const tmp = [PROTOCOL.OPERATION.DATA_ACK, PROTOCOL.OPERATION.DATA_TRANSFER]
      if (tmp.includes(view.getUint8(1)) && tmp.includes(operation)) {
        return;
      } else {
        throw new Error(`Invalid operation code: expected ${operation}, got ${view.getUint8(1)}`);
      }
    }
    if (view.getUint8(0) !== PROTOCOL.COMMAND.RESPONSE) {
      throw new Error('Invalid response header');
    }
  }
}

export class RemoteBluetooth extends BasicRemoteBluetooth {
  async readRemoteControllerVersionInfo() {
    try {
      const run = () => this.startCommand(Operation.READ, { type: RemoteControllerInfoType.VERSION, length: 16 });
      const result = await this.withResponseHandler(run,
        (data, succeed, fail) => {
          try {
            const view = new DataView(data.value);
            const operation = view.getUint8(1);
            switch (operation) {
              case PROTOCOL.OPERATION.READ:
                const { status } = this.parseResponse(data.value, Operation.READ);
                if (status !== OperationStatus.SUCCESS) {
                  fail(`读取失败，状态码: 0x${status.toString(16)}`);
                }
                break;
              case PROTOCOL.OPERATION.DATA_TRANSFER:
              case PROTOCOL.OPERATION.DATA_ACK:
                const transferData = this.parseResponse(data.value, Operation.DATA_TRANSFER);
                const fullData = this.packetSplitter.processReceivedPacket(
                  {
                    sequence: transferData.packetSequence,
                    length: transferData.dataLength,
                    data: transferData.packetData
                  },
                  data.deviceId
                );
                if (!fullData) {
                  return; // 数据未完成，继续等待数据
                }
                if (fullData.errors.length > 0) {
                  fail(fullData.errors.join(','));
                } else {
                  succeed(fullData.data);
                }
                break;
              default:
                console.log('_readData 未知操作:', this.arrayBufferToHexString(data.value));
                break;
            }
          } catch (error) {
            fail(error instanceof Error ? error : new Error('处理数据时发生未知错误'));
          }
        })
      this.startCommand(Operation.TERMINATE, { crc: false });
      return this.arrayBufferToHexString(result);
    } catch (error) {
      throw error;
    }
  }

  async transferBasicInfo(list: Array<{ type: RemoteControllerInfoType; data: string }>) {
    const hostAuthRes = await this._setUpgradeFlag(true)
    if (hostAuthRes) {
      await this._authenticateHost(hostAuthRes);
    }
    for await (const { type, data } of list) {
      const bytes = data.split(' ').map(byte => parseInt(byte, 16));
      const uint8Array = new Uint8Array(bytes);
      await this._writeData(type, uint8Array);
    }
    await this._setUpgradeFlag(false);
    return;
  }

  private async _writeData(type: RemoteControllerInfoType, data: Uint8Array) {
    const { status } = await this.executeCommandWithResponse(
      Operation.WRITE,
      {
        type,
        length: MAX_CHUNK_SIZE, // 最大块大小
        dataLength: data.length
      }
    );
    if (status !== OperationStatus.SUCCESS) {
      throw new Error(`启动写失败: 0x${status.toString(16)}`);
    }
    await this.sendRemoteData(data);
    const crc = calculateCRC16(data);
    await this.executeCommandWithResponse(
      Operation.TERMINATE,
      { crc: true, crcValue: crc }
    );
  }

  private async _setUpgradeFlag<T extends boolean>(
    enable: T
  ) {
    if (enable) {
      const commandRun = () => this.startCommand(Operation.SET_FLAG, { flag: UpgradeFlag.VALID });
      return this.withResponseHandler(commandRun, (data, succeed, fail) => {
        try {
          const view = new DataView(data.value);
          const operation = view.getUint8(1);
          switch (operation) {
            case PROTOCOL.OPERATION.SET_FLAG:
              const { status } = this.parseResponse(data.value, Operation.SET_FLAG);
              if (status !== OperationStatus.SUCCESS) {
                fail(`设置升级标志失败: 0x${status.toString(16)}`);
              }
              break;
            case PROTOCOL.OPERATION.HOST_AUTH:
              succeed(data.value);
              break;
            default:
              console.log('_setUpgradeFlag 未知操作:', this.arrayBufferToHexString(data.value));
              break;
          }
        } catch (error) {
          fail(error instanceof Error ? error : new Error('处理数据时发生未知错误'));
        }
      }, {
        timeout: 500
      }).catch(err => {
        if (err.message.includes('操作超时')) {
          //微纳认证过后不会再次认证 故而超时算成功
          console.log('err', err)
        } else {
          throw err
        }
      })
    }
    const { status } = await this.executeCommandWithResponse(
      Operation.SET_FLAG,
      { flag: UpgradeFlag.NONE }
    );
    if (status !== OperationStatus.SUCCESS) {
      throw new Error(`设置升级标志失败: 0x${status.toString(16)}`);
    }
  }

  private async _authenticateHost(hostAuth: ArrayBuffer) {
    const {
      status: hostStatus,
      keyIndex = 0,
      rand: rand1,
      idCode
    } = this.parseResponse(hostAuth, Operation.HOST_AUTH);
    // 不走认证
    return;
    if (hostStatus === AuthStatus.SUCCESS) {
      return
    }
    const key = this._getKeyByIndex(keyIndex);
    const hostHash = this._calculateAuthHash(key, rand1!, idCode!);
    console.log(this.arrayBufferToHexString(hostHash.buffer))
    const { status: hostAuthStatus } = await this.executeCommandWithResponse(Operation.HOST_AUTH, { hash: hostHash })
    console.log('hostAuthStatus', hostAuthStatus);
    if (hostAuthStatus === AuthStatus.FAILED) {
      throw new Error('主机认证失败');
    }
    // a9 8d 4c 33 df 7a c5 89 52 60 45 e4 87 3e 1f 84
    // ===== 第二步：设备认证 =====
    const deviceRand = this.stringToUint8Array(this._generateRandom());
    const { hash: deviceHash } = await this.executeCommandWithResponse(Operation.DEVICE_AUTH, {
      status: AuthStatus.INITIATE,
      keyIndex,
      random: deviceRand
    });
    const expectedHash = this._calculateAuthHash(key, deviceRand, idCode!);
    if (!this._compareHashes(expectedHash, deviceHash)) {
      this.startCommand(Operation.DEVICE_AUTH, {
        keyIndex,
        random: deviceRand,
        status: AuthStatus.FAILED
      });
      throw new Error("设备认证失败");
    }
    this.startCommand(Operation.DEVICE_AUTH, {
      status: AuthStatus.SUCCESS,
      keyIndex,
      random: deviceRand,
    });
    await this.sleep(100); // 等待 100 ms 遥控器区别有的会有响应有的没有
  }

  private _generateRandom() {
    return '5246373975745035'
    // const randomInput = Date.now() + Math.random().toString(36).substring(2);
    // // 2. 使用 SparkMD5 计算 Hash
    // const hash = SparkMD5.hash(randomInput);
    // // 3. 取前 16 位（或后 16 位）
    // return hash.substring(0, 16); // 32 位 MD5 的前 16 位
  }

  private _getKeyByIndex(index: number) {
    const keyMap = ["5986886105823165",
      "4896369745799370",
      "6123911824786770",
      "9180518818574041",
    ]
    return keyMap[index];
  }

  private _calculateAuthHash(
    key: string,
    random: Uint8Array | string,
    idCode: Uint8Array
  ) {
    const randomBytes = typeof random === 'string' ? this.stringToUint8Array(random) : random;
    const _combined = new Uint8Array([
      ...this.stringToUint8Array(key),
      ...randomBytes,
      ...idCode
    ]);
    const md5 = new SparkMD5.ArrayBuffer();
    md5.append(_combined.buffer);
    const hashHex = md5.end();
    // 将 Hex 字符串转为 16 字节 Uint8Array
    const hashBytes = new Uint8Array(16);
    for (let i = 0; i < 16; i++) {
      hashBytes[i] = parseInt(hashHex.substr(i * 2, 2), 16);
    }
    return hashBytes;
  }

  private _compareHashes(hash1: Uint8Array, hash2: Uint8Array): boolean {
    return hash1.every((byte, i) => byte === hash2[i]);
  }
}

export const remoteBluetooth = new RemoteBluetooth({
  deviceNameFilter: /.*/,
  rssiThreshold: -200,
  splitterOptions: {
    chunkSize: MAX_CHUNK_SIZE,
  },
});

